package nus.iss.ga.controller.model;

import java.util.ArrayList;
import java.util.List;

import nus.iss.ga.controller.common.IGAController;

import org.jgap.FitnessFunction;
import org.jgap.IChromosome;
import org.jgap.impl.IntegerGene;

/**
 * @author Fum Kok Siong
 * 
 */
public class ContainerFitnessFunction extends FitnessFunction {

	private static final long serialVersionUID = 3041351003857322853L;
	private List<Container> list;
	private double averageWeight = 0.0d;
	private double penaltyForHeavierAtTop = 1.0d;
	private double penaltyForItemType = 1.0d;
	private double penaltyForItemState = 1.0d;
	private double penaltyForDestination = 10000.0d;
	private int nrOfC;
	private int nrOfR;
	private int nrOfS;

	public ContainerFitnessFunction(IGAController gaController, List<Container> list) {
		super();
		this.list = new ArrayList<Container>(list);
		this.nrOfC = gaController.getNumberOfC();
		this.nrOfR = gaController.getNumberOfR();
		this.nrOfS = gaController.getNumberOfS();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.FitnessFunction#evaluate(org.jgap.IChromosome)
	 */
	@Override
	protected double evaluate(IChromosome chromosome) {

		double squaredDiff = 0.0d;
		for (int c = 0; c < nrOfC; c++) {
			for (int s = 0; s < nrOfS; s++) {
				double groupWeight = 0.0d;
				for (int r =0; r < nrOfR; r++) {
					IntegerGene containerIndex = (IntegerGene) chromosome.getGene(c * 16 + s * 4 + r);

					Container container = (Container) list.get(containerIndex.intValue());
					groupWeight += container.getWeight();
				}
				double diff = Math.abs(groupWeight - averageWeight);
				squaredDiff = diff * diff;
			}
		}
		
		squaredDiff += getPenalty(chromosome);
		squaredDiff += getPenaltyForDestination(chromosome);
		
		return squaredDiff;
	}

	public void setAverageWeight(double val) {
		this.averageWeight = val;
		
	}
	
	private double getPenaltyForDestination(IChromosome chromosome) {
		double penalty = 0.0d;
		for (int i = 0; i < (nrOfC * nrOfR * nrOfS - 1); i++) {
			IntegerGene innerContainerIndex = (IntegerGene) chromosome.getGene(i);
			Container innerContainer = (Container) list.get(innerContainerIndex.intValue());

			IntegerGene outterContainerIndex = (IntegerGene) chromosome.getGene(i + 1);
			Container outterContainer = (Container) list.get(outterContainerIndex.intValue());
			
			if (innerContainer.getDestination().compareTo(outterContainer.getDestination()) < 0) {
				penalty += penaltyForDestination;
			}
		}
		
		return penalty;

	}

	private double getPenalty(IChromosome chromosome) {
		double weightPenalty = 0;
		double itemStatePenalty = 0.0d;
		double itemTypePenalty = 0.0d;
		
		for (int c = 0; c < nrOfC; c++) {
			for (int s = 0; s < nrOfS; s++) {
				for (int r = 0; r < (nrOfR - 1); r++) {
					IntegerGene lowerContainerIndex = (IntegerGene) chromosome.getGene(c * 16 + s * 4 + r);
					Container lowerContainer = (Container) list.get(lowerContainerIndex.intValue());

					IntegerGene higherContainerIndex = (IntegerGene) chromosome.getGene(c * 16 + s * 4 + r + 1);
					Container higherContainer = (Container) list.get(higherContainerIndex.intValue());

					if (lowerContainer.getWeight() < higherContainer.getWeight()) {
						weightPenalty += penaltyForHeavierAtTop;
					}
					
					if (lowerContainer.getStateOfPackage().compareTo(higherContainer.getStateOfPackage()) > 0) {
						itemStatePenalty += penaltyForItemState;
					}
					
					if (lowerContainer.getTypeOfPackage().compareTo(higherContainer.getTypeOfPackage()) > 0) {
						itemTypePenalty += penaltyForItemType;
					}
				}
			}
		}
//		System.out.println("getPenaltyForHeavierAtTop : " + weightPenalty);
		return weightPenalty + itemStatePenalty + itemTypePenalty;
	}

	public void setPenaltyForHeavierAtTop(double penaltyForHeavierAtTop) {
		this.penaltyForHeavierAtTop = penaltyForHeavierAtTop;
	}

	public void setPenaltyForItemType(double penaltyForItemType) {
		this.penaltyForItemType = penaltyForItemType;
	}

	public void setPenaltyForItemState(double penaltyForItemState) {
		this.penaltyForItemState = penaltyForItemState;
	}

	public void setPenaltyForDestination(double penaltyForDestination) {
		this.penaltyForDestination = penaltyForDestination;
	}
}
